





#include   "ShaderPrograms/GlSLShaderProgram.h"
#include   "FileUtils/Data.h"

namespace gengine
{
	namespace kernel
	{


GlShaderProgram ::GlShaderProgram()
{
	program        = 0;
    vertexShader   = 0;
    fragmentShader = 0;
	geometryShader = 0;
    ok             = false;
}

GlShaderProgram :: ~GlShaderProgram ()
{
    Clear ();
}


bool    GlShaderProgram ::LoadShaders ( const char * vertexFileName, const char * fragmentFileName ,const char * geometryFileName)
{
	 ok = false;
	bool result = true;
	
	program = glCreateProgramObjectARB();  
	
	if(vertexFileName!=0)
	{
		vertexShader = glCreateShader(GL_VERTEX_SHADER);
		LoadShader(vertexShader,vertexFileName);

		glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(vertexShader);
		  
			
			result = false;
		}
		glAttachShader(program,vertexShader);
	}
	
	
	if(fragmentFileName!=0)
	{
		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		
		LoadShader(fragmentShader,fragmentFileName);

		glGetShaderiv(fragmentShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(fragmentShader);
			 
			
			result = false;
		}
		glAttachShader(program,fragmentShader);
	}

	if(geometryFileName!=0)
	{
		geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
	    LoadShader(geometryShader,geometryFileName);
		glGetShaderiv(geometryShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(geometryShader);
			
			
			result = false;
		}
		glAttachShader(program,geometryShader);
	}
  
	

   glLinkProgramARB(program);
  
	
 

    return result;
}

/*bool GlShaderProgram ::SetShaderSources(const char*vertexSrc,const char*fragmentSrc,const char*geometrySrc )
{
	  
	  bool result = true;
	
	 
	
	
	program = glCreateProgramObjectARB();


	 
	  if(vertexSrc!= 0)
	  {
	  const  char *vsStringPtr[1] ;
	 
	  vsStringPtr[0] = vertexSrc;
	  vertexShader = glCreateShader(GL_VERTEX_SHADER);
	  glShaderSourceARB(vertexShader,1, vsStringPtr,0);
	  glCompileShaderARB(vertexShader);

	    glGetShaderiv(vertexShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(vertexShader);
		 
			
			
			result = false;
		}
		glAttachShader(program,vertexShader);
	  }
	  
	  if(fragmentSrc!= 0)
	  {
	  const  char *fsStringPtr[1] ;
	  fsStringPtr[0] = fragmentSrc;
	  fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	  glShaderSourceARB(fragmentShader,1, fsStringPtr,0);
	  glCompileShaderARB(fragmentShader);

	    glGetShaderiv(fragmentShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(fragmentShader);
			
			result = false;
		}
		glAttachShader(program,fragmentShader);
	  }
	
	  if(geometrySrc!=0)
	  {
      const  char *geomStringPtr[1] ;
	  geomStringPtr[0] = geometrySrc;
	  geometryShader = glCreateShader(GL_GEOMETRY_SHADER);
	  glShaderSourceARB(geometryShader,1, geomStringPtr,0);
	  glCompileShaderARB(geometryShader);

	    glGetShaderiv(geometryShader,GL_COMPILE_STATUS,&shaderCompileStatus);
		if(shaderCompileStatus == GL_FALSE)
		{   
			LoadLog(geometryShader);
			
			result = false;
		}
		glAttachShader(program,geometryShader);
	  }
    
	glLinkProgramARB(program);
	return true;
}*/



void    GlShaderProgram :: Bind ()
{   
    glUseProgramObjectARB ( program );


}

void    GlShaderProgram :: Unbind ()
{
    glUseProgramObjectARB ( 0 );
}



bool    GlShaderProgram :: CheckGlError ()
{
    bool    retCode = true;
 	GLenum  glErr   = glGetError();

    if ( glErr == GL_NO_ERROR )
    	return retCode;

  //  glError = (const char *) gluErrorString ( glErr );		// XXX: what about gluErrorString errors ?

    return false;
}

void    GlShaderProgram :: Clear ()
{
    glDeleteObjectARB ( program        );                   // it will also detach shaders
    glDeleteObjectARB ( vertexShader   );
    glDeleteObjectARB ( fragmentShader );
	glDeleteObjectARB ( geometryShader );


    program        = 0;
    vertexShader   = 0;
    fragmentShader = 0;
	geometryShader = 0;
    ok             = false;
}

void    GlShaderProgram :: LoadLog ( GLhandleARB object )
{
    int         logLength     = 0;
    int         charsWritten  = 0;
    GLcharARB   buffer [2048];
    GLcharARB * infoLog;

    glGetObjectParameterivARB ( object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &logLength );

  

    if ( logLength < 1 )
        return;
                                    // try to avoid allocating buffer
    if ( logLength > sizeof ( buffer ) )
    {
        infoLog = (GLcharARB*) malloc ( logLength );

        if ( infoLog == NULL )
        {
            log = "ERROR: Could not allocate log buffer";

            return;
        }
    }
    else
        infoLog = buffer;

    glGetInfoLogARB ( object, logLength, &charsWritten, infoLog );

    log += infoLog;

    if ( infoLog != buffer )
        free ( infoLog );
}

bool    GlShaderProgram :: SetConstant ( const char * name, const Vector4D& value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform4fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( int loc, const Vector4D& value )
{
    glUniform4fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( const char * name, const Vector3D& value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform3fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( int loc, const Vector3D& value )
{
    glUniform3fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( const char * name, const Vector2D& value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform2fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( int loc, const Vector2D& value )
{
    glUniform2fvARB ( loc, 1, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( const char * name, float value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform1fARB ( loc, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( int loc, float value )
{
    glUniform1fARB ( loc, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( const char * name, int value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniform1iARB ( loc, value );

    return true;
}

bool    GlShaderProgram :: SetConstant ( int loc, int value )
{
    glUniform1iARB ( loc, value );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( const char * name, const Matrix4x4& value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniformMatrix4fvARB ( loc, 1, GL_FALSE, value [0] );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( const char * name, float value [16] )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniformMatrix4fvARB ( loc, 1, GL_FALSE, value );

    return true;
}

bool    GlShaderProgram :: SetConstant  ( const char * name, const Matrix3D& value )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return false;

    glUniformMatrix3fvARB ( loc, 1, GL_FALSE, value [0] );

    return true;
}

int     GlShaderProgram :: LocForUniformName ( const char * name )
{
    return glGetUniformLocationARB ( program, name );
}

Vector4D    GlShaderProgram :: GetUniformVector ( const char * name )
{
    float   values [4];

    int loc = glGetUniformLocationARB ( program, name );

    if ( loc < 0 )
        return Vector4D ( 0, 0, 0, 0 );

    glGetUniformfvARB ( program, loc, values );

    return Vector4D ( values [0], values [1], values [2], values [3] );
}

bool    GlShaderProgram :: SetTexture ( const char * name, unsigned texUnit )
{
    int loc = glGetUniformLocationARB ( program, name );

    if ( loc == -1 )
        return false;

    glUniform1iARB ( loc, texUnit );

    return true;
}

bool    GlShaderProgram :: SetTexture ( int loc, int texUnit )
{
    if ( loc < 0 )
        return false;

    glUniform1iARB ( loc, texUnit );

    return true;
}



////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool    GlShaderProgram :: BindAttributeTo  ( int loc, const char * name )
{
    glBindAttribLocationARB ( program, loc, name );

    return true;
}



bool    GlShaderProgram :: SetAttribute ( const char * name, const Vector4D& value )
{
    int index = glGetAttribLocationARB ( program, name );

    if ( index < 0 )
        return false;

    glVertexAttrib4fvARB ( index, value );

    return true;
}

bool    GlShaderProgram :: SetAttribute ( int index, const Vector4D& value )
{
    glVertexAttrib4fvARB ( index, value );

    return true;
}

int     GlShaderProgram :: IndexForAttrName ( const char * name )
{
    return glGetAttribLocationARB ( program, name );
}

Vector4D    GlShaderProgram :: GetAttribute ( const char * name )
{
    int index = glGetAttribLocationARB ( program, name );

    if ( index < 0 )
        return Vector4D ( 0, 0, 0, 0 );

    float   buf [4];

    glGetVertexAttribfvARB ( index, GL_CURRENT_VERTEX_ATTRIB_ARB, buf );

    return Vector4D ( buf [0], buf [1], buf [2], buf [3] );
}

Vector4D    GlShaderProgram :: GetAttribute ( int index )
{
    float   buf [4];

    glGetVertexAttribfvARB ( index, GL_CURRENT_VERTEX_ATTRIB_ARB, buf );

    return Vector4D ( buf [0], buf [1], buf [2], buf [3] );
}

bool    GlShaderProgram :: IsSupported ()
{
    return isExtensionSupported ( "GL_ARB_shading_language_100" ) &&
           isExtensionSupported ( "GL_ARB_shader_objects"       ) &&
           isExtensionSupported ( "GL_ARB_vertex_shader"        ) &&
           isExtensionSupported ( "GL_ARB_fragment_shader"      );

}

string  GlShaderProgram :: Version ()
{
    const char * slVer = (const char *) glGetString ( GL_SHADING_LANGUAGE_VERSION_ARB );

    if ( glGetError() != GL_NO_ERROR )
        return "1.051";

    return string ( slVer );
}

int     GlShaderProgram :: MaxVertexUniformComponents ()
{
    int maxVertexUniformComponents;

    glGetIntegerv ( GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &maxVertexUniformComponents );

    return maxVertexUniformComponents;
}

int     GlShaderProgram :: MaxVertexAttribs ()
{
    int maxVertexAttribs;

    glGetIntegerv ( GL_MAX_VERTEX_ATTRIBS_ARB, &maxVertexAttribs );

    return maxVertexAttribs;
}

int     GlShaderProgram :: MaxFragmentTextureUnits ()
{
    int maxFragmentTextureUnits;

    glGetIntegerv ( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxFragmentTextureUnits );

    return maxFragmentTextureUnits;
}

int     GlShaderProgram :: MaxVertexTextureUnits ()
{
    int maxVertexTextureUnits;

    glGetIntegerv ( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &maxVertexTextureUnits );

    return maxVertexTextureUnits;
}

int     GlShaderProgram :: MaxCombinedTextureUnits ()
{
    int maxCombinedTextureUnits;

    glGetIntegerv ( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB, &maxCombinedTextureUnits );

    return maxCombinedTextureUnits;
}

int     GlShaderProgram :: MaxVaryingFloats ()
{
    int maxVaryingFloats;

    glGetIntegerv ( GL_MAX_VARYING_FLOATS_ARB, &maxVaryingFloats );


    return maxVaryingFloats;
}

int     GlShaderProgram :: MaxFragmentUniformComponents ()
{
    int maxFragmentUniformComponents;

    glGetIntegerv ( GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB, &maxFragmentUniformComponents );

    return maxFragmentUniformComponents;
}

int     GlShaderProgram :: MaxTextureCoords ()
{
    int maxTextureCoords;

    glGetIntegerv ( GL_MAX_TEXTURE_COORDS_ARB, &maxTextureCoords );

    return maxTextureCoords;
}



bool GlShaderProgram::LoadShader(GLhandleARB shader,const char *filename)
{
	  const  char *vsStringPtr[1];
	
	utils::Data *data = utils::Data::getDataFromFile(filename);
	
	
	vsStringPtr[0] = data->getString().c_str();
	glShaderSourceARB(shader,1, vsStringPtr,0);
	glCompileShaderARB(shader);

	delete data;

	return true;
}
}
}